#include "SocketCommunicator.h"
#include "ScreenHandler.h"
#include "WorldHandler.h"
#include "ServerHandler.h"
#include "Logging.h"
#include "Config.h"
#include "NetworkHandler.h"

#include <sstream>
#include <stdlib.h>
#include <time.h>

SocketCommunicator::SocketCommunicator()
{
	//fars
}

void SocketCommunicator::startCommunication()
{
	if(globalConfig.getIsServer() == true)
	{
		socket = new udp::socket(ioService, udp::endpoint(udp::v4(), 49998));
		logMessage("Server listening at port 49998."); //
		asyncReceive();
		
		while(/*!stopRequested*/1)
		{
		 	ioService.run_one();
			USLEEP(1);
		}
	}
	else
	{
		serverIP = globalConfig.getServerIP();
	
		serverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(serverIP), 49998);
		srand((unsigned)time(NULL));
		port = rand()%64511+1024;
		udp::endpoint localEndPoint = udp::endpoint(boost::asio::ip::address::from_string("0.0.0.0"), port);
		socket = new udp::socket(ioService);
		socket->open(localEndPoint.protocol()); 
		socket->bind(localEndPoint);
		socket->connect(serverEndpoint);

		//std::string localPlayerName = globalConfig.getLocalPlayerName();
		//std::vector<NetworkPacket> initBundle;
		//initBundle.push_back(std::make_pair(INIT, localPlayerName));
		std::string playerName = globalConfig.getLocalPlayerName();
		globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(&playerName), INIT);
		//		sendSync(serializer.callSerialize(reinterpret_cast<uintptr_t>(&initBundle), PACKETBUNDLE));
		
		while(/*!stopRequested*/1)
		{
			syncReceive();
		}
		
		socket->close();
	}
}

void SocketCommunicator::asyncReceive()
{
  	socket->async_receive_from(
    boost::asio::buffer(recvBuffer), clientEndpoint,
    boost::bind(&SocketCommunicator::handleReceive, this,
    boost::asio::placeholders::error,
    boost::asio::placeholders::bytes_transferred));
}

void SocketCommunicator::syncReceive()
{
	size_t bytes_transferred;
	bool success = true;

	static unsigned int error_count;
	
	try
	{
		bytes_transferred = socket->receive(boost::asio::buffer(recvBuffer));
	}
	catch(boost::system::system_error& e)
	{
		++error_count;
		success = false;
	}
	
	if(success)
	{
		error_count = 0;
		if(/*stopRequested*/0)
		{
			logMessage("Stopping SocketCommunicator thread.");
		}
		else
		{
			//FIXME: ClientAddress should not be used here
			//NetworkPacket newPacket;
			//serializer.callDeserialize(reinterpret_cast<uintptr_t>(&newPacket), NETWORKPACKET, reinterpret_cast<char*>(&recvBuffer));
			//receivedPackets.push_back(std::make_pair(newPacket, clientAddress));

			ClientAddress clientAddress = {"", 0};
			
			std::string newPacket = reinterpret_cast<char*>(&recvBuffer); 
			recvPacket.append(newPacket.substr(0, bytes_transferred));
			std::cout << "Received " << bytes_transferred << " bytes.\n";
			
			if(bytes_transferred < 1024)
			{
				globalCSHandler->addToReceivedQueue(recvPacket, &clientAddress);
				recvPacket.clear();
			}

			//ClientHandler()->interruptWait();
		}
	}
	else
	{
		if(error_count > 5)
		{
			std::cout << "Timeout to server!\n";
			error_count = 0;
			//do stuff;
		}
	}
}

void SocketCommunicator::handleReceive(const boost::system::error_code& error, std::size_t bytes_transferred)
{
	if (!error || error == boost::asio::error::message_size)
	{		
		ClientAddress clientAddress;
		std::stringstream portSS;
		clientAddress.ip = clientEndpoint.address().to_string();
		clientAddress.port = clientEndpoint.port();
		
//		logMessage("RECEIVED DATA FROM " + clientIP + ":" + portTest.str() + " - " + reinterpret_cast<char*>(&recvBuffer));

		globalCSHandler->addToReceivedQueue(reinterpret_cast<char*>(&recvBuffer), &clientAddress);
		
//		serializer.callDeserialize(reinterpret_cast<uintptr_t>(&newPacket), NETWORKPACKET, reinterpret_cast<char*>(&recvBuffer));
//		receivedPackets.push_back(std::make_pair(newPacket, clientAddress));
		//receivedMessages.push_back(clientIP + ":" + portTest.str() + "\n");
	}
	asyncReceive();
}

void SocketCommunicator::handleSend(std::string* message, const boost::system::error_code& error, std::size_t bytes_transferred)
{
//	logMessage("SENT data");
	delete message;
}

void SocketCommunicator::start()
{
    thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&SocketCommunicator::startCommunication, this)));
}

void SocketCommunicator::stop()
{
	//stopRequested = true;
	ioService.stop();

	int n = static_cast<int>(boost::posix_time::time_duration::ticks_per_second()) / 10;
    boost::posix_time::time_duration delay(0, 0, 0, n); //linus boostvalds
	thread->timed_join(delay);
}

bool SocketCommunicator::sendAsync(ClientAddress* passedClientAddress, const std::string& message)
{
	boost::asio::ip::udp::endpoint curClientEndpoint(boost::asio::ip::address::from_string(passedClientAddress->ip), passedClientAddress->port);
	
	//message->append("|END|");
	
	for(size_t pos = 0; pos < message.length(); pos += 1024)
	{
		std::string* data = new std::string(message.substr(pos, 1024));
		
		socket->async_send_to(boost::asio::buffer(*data), curClientEndpoint, 
		boost::bind(&SocketCommunicator::handleSend, this, data,
		boost::asio::placeholders::error,
		boost::asio::placeholders::bytes_transferred));		
	}
	


	return true;
}

void SocketCommunicator::sendSync(const std::string& message)
{
	if(/*!stopRequested*/1)
	{
		socket->send(boost::asio::buffer(message));
		//logMessage("SENT '" + message + "' to server."); 
	}
}

boost::asio::io_service* SocketCommunicator::getIOService()
{
	return &ioService;
}
